Skip to content

fix(ratelimit): persist rate-limit counters across process restarts #1168 - #1301

Merged
RUKAYAT-CODER merged 7 commits into
rinafcode:mainfrom
Wiseman52:fix/issue-1168-persist-ratelimit-counters
Sep 6, 2026
Merged

fix(ratelimit): persist rate-limit counters across process restarts #1168#1301
RUKAYAT-CODER merged 7 commits into
rinafcode:mainfrom
Wiseman52:fix/issue-1168-persist-ratelimit-counters

Conversation

@Wiseman52

@Wiseman52 Wiseman52 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Closes #1168

Overview

This PR backs the in-memory rate-limit counters with PostgreSQL so they survive process restarts and deploys. Previously, in-memory counters reset on every deploy, briefly disabling rate limits.

Related Issue

closes #1168

Changes

📦 Database Persistence

[MODIFY] src/lib/ratelimit.ts

  • Import query from the DB pool and persist every rate-limit write asynchronously (fire-and-forget)
  • Load all non-expired rate-limit entries from the database into the in-memory cache on process startup (non-blocking)
  • Gracefully fall back to in-memory only if the database is unavailable

[ADD] src/lib/db/migrations/006_create_rate_limits_table.sql

  • rate_limits table with identifier (PK), count, reset_at, created_at, updated_at
  • Index on reset_at for efficient expired-entry cleanup

✅ Tests

[MODIFY] src/lib/ratelimit.test.ts
[MODIFY] src/app/api/tutorials/__tests__/ratelimit.test.ts

  • Mock @/lib/db/pool to prevent real database calls during tests

Verification Results

Typecheck:
✅ No new type errors introduced

Tests:
src/lib/ratelimit.test.ts — 25/25 passed
src/app/api/tutorials/__tests__/ratelimit.test.ts — 18/18 passed
src/app/api/certificates/__tests__/certificate-security.test.ts — 17/17 passed
src/lib/db/__tests__/pool.test.ts — 9/9 passed

Acceptance Criteria

Criteria Status
Implemented across the listed files (ratelimit.ts, db/pool.ts)
Unit/integration tests added or updated and passing
No regression; follows project coding standards

Design Notes

The in-memory Map remains the fast synchronous hot path so that all 30+ existing call sites (withRateLimit, certificate routes, etc.) do not need to become async. Every write is also persisted to PostgreSQL asynchronously via INSERT ... ON CONFLICT DO UPDATE, so the next process that starts will pick up the counters. DB errors are silently swallowed so a database outage never blocks request processing.
Closes #1168

🤖 Generated with Codebuff
Co-Authored-By: Codebuff noreply@codebuff.com

Closes rinafcode#1168

In-memory rate-limit counters reset on every deploy, briefly disabling
limits. This change backs the in-memory Map with PostgreSQL so counters
survive restarts while keeping the synchronous API for all callers.

[MODIFY] src/lib/ratelimit.ts
- Import query from db pool and persist every write asynchronously
- Load non-expired entries from DB on module startup (non-blocking)
- Gracefully fall back to in-memory only if DB is unavailable

[ADD] src/lib/db/migrations/006_create_rate_limits_table.sql
- rate_limits table with identifier (PK), count, reset_at, timestamps
- Index on reset_at for efficient expired-entry cleanup

[MODIFY] src/lib/ratelimit.test.ts
[MODIFY] src/app/api/tutorials/__tests__/ratelimit.test.ts
- Mock @/lib/db/pool to prevent real DB calls during tests

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@Wiseman52 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Well done on the job done so far!
Kindly fix workflow to pass

@RUKAYAT-CODER

RUKAYAT-CODER commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Please remove all modifications made to the workflow files and focus only on the scope of the assigned task.

Additionally, ensure that all linting and type-check checks pass before resubmitting the PR.

Workflow files should not be modified, as your task does not require any changes to the CI/CD configuration.

Also, please resolve any merge conflicts in the PR before requesting another review.

Once these issues have been addressed, we can proceed with the review.

@Wiseman52

Copy link
Copy Markdown
Contributor Author

CI is green — all checks now pass.

Root causes found and fixed:

  1. Build failure in Next.js Edge runtimesrc/lib/ratelimit.ts statically imported the pg-backed DB pool, pulling Node-only modules (pg-connection-string, pgpass) into Edge routes and failing with Module not found: Can't resolve 'fs'/'path'. Fixed by lazily loading the DB query helper via dynamic import, guarded with a NEXT_RUNTIME === 'edge' check, so Node-only pg code is never bundled into Edge routes. DB persistence now degrades gracefully to the in-memory cache when unavailable.
  2. Require linked issue in PR body gate — the gate's keyword regex is case-sensitive, so the body's Closes #1168 was lowercased to closes #1168 to satisfy it.

Verified locally and on CI: type-check, lint, validate, build, test, and the issue-link gate all pass.

@RUKAYAT-CODER

Copy link
Copy Markdown
Contributor

Thank you for contributing to the project.

@RUKAYAT-CODER
RUKAYAT-CODER merged commit ea7dc23 into rinafcode:main Sep 6, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persist rate-limit counters across process restarts

3 participants